package com.aaa.lib_compiler.my;

import com.aaa.lib_annotations.my.MyAnnotation;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;

@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(MyAnnotation.class.getCanonicalName());
        return annotations;
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        // 唯独process()方法不被执行的原因 todo getSupportedAnnotationTypes及App模块中需要使用相同的注解
        { // 如何用 javapoet 生成一个类 https://blog.csdn.net/crazy1235/article/details/51876192
            String packageName = "com.aaa.javapoet.my222";
//              String className = "HelloWorld";
            String className = "HelloWorld_my";
            MethodSpec constructor = MethodSpec.constructorBuilder()
                    .addModifiers(Modifier.PUBLIC)
                    .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
                    .build();
//              ClassName clazzName = ClassName.get("com.example.adc", "df"); // 也可以
            ClassName clazzName = ClassName.get(packageName, className);
            MethodSpec other = MethodSpec.methodBuilder("other")
                    .addModifiers(Modifier.PUBLIC)
                    .returns(clazzName)
                    .addStatement("return new $T()", clazzName)
                    .build();
            MethodSpec main = MethodSpec.methodBuilder("main")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .returns(void.class)
                    .addParameter(String[].class, "args")
                    .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
                    .build();
            TypeSpec helloWorld = TypeSpec.classBuilder(className)
                    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                    .addMethod(constructor)
                    .addMethod(other)
                    .addMethod(main)
                    .build();
            JavaFile javaFile = JavaFile.builder(packageName, helloWorld)
                    .build();
            try {
                javaFile.writeTo(processingEnv.getFiler());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        {
            Map<String, List<MyAnnotatedElement>> mQualifiedAnnotatedElementMap = new LinkedHashMap<>();
            Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(MyAnnotation.class);
            for (Element element : elements) {
                MyAnnotatedElement myAnnotatedElement = new MyAnnotatedElement(element);
                String qualifier = myAnnotatedElement.getQualifiedClassName().toString();
                if (mQualifiedAnnotatedElementMap.get(qualifier) == null) {
                    mQualifiedAnnotatedElementMap.put(qualifier, new ArrayList<MyAnnotatedElement>());
                }
                mQualifiedAnnotatedElementMap.get(qualifier).add(myAnnotatedElement);
            }
            if (mQualifiedAnnotatedElementMap.size() > 0) {
                for (Map.Entry<String, List<MyAnnotatedElement>> entry : mQualifiedAnnotatedElementMap.entrySet()) {
                    MyAnnotatedElement myAnnotatedElement = entry.getValue().get(0);
                    ClassName classM = ClassName.get(myAnnotatedElement.getPackageName(),
                            myAnnotatedElement.getSimpleClassName().toString() + "_suffix");
                    ClassName classN = ClassName.get(myAnnotatedElement.getPackageName(),
                            myAnnotatedElement.getSimpleClassName().toString());
                    MethodSpec.Builder methodSpecBuilder = MethodSpec.constructorBuilder()
                            .addModifiers(Modifier.PUBLIC)
                            .addParameter(classN, "obj");
                    for (MyAnnotatedElement element : entry.getValue()) {
                        methodSpecBuilder.addStatement(String.format(
                                "obj.%1$s = %2$s",
                                element.getElementName().toString(),
                                element.getElement().getAnnotation(MyAnnotation.class).strValue())
                        );
                    }
                    TypeSpec typeSpec = TypeSpec.classBuilder(classM)
                            .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                            .addMethod(methodSpecBuilder.build())
                            .build();
                    JavaFile javaFile = JavaFile.builder(myAnnotatedElement.getPackageName(), typeSpec)
                            .build();
                    try {
                        javaFile.writeTo(processingEnv.getFiler());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        return true;
    }
}
